package com.dbflow5.processor.utils;

import com.dbflow5.StringUtils;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.MethodSpec;

import java.util.function.Function;

/**
 * Description: Set of utility methods to save code
 *
 * @author Andrew Grosner (fuzz)
 */
public class CodeExtensions{
    /**
     * Collapses the control flow into an easy to use block
     *
     * @param builder builder
     * @param statement statement
     * @param method method
     * @param args args
     * @return control flow builder
     */
    public static CodeBlock.Builder controlFlow(CodeBlock.Builder builder, String statement, Function<CodeBlock.Builder, Void> method, Object... args) {
        CodeBlock.Builder newBuilder = builder.beginControlFlow(statement, args);
        method.apply(newBuilder);
        return newBuilder.endControlFlow();
    }

    public static MethodSpec.Builder controlFlow(MethodSpec.Builder builder, String statement, Function<CodeBlock.Builder, Void> method, Object... args) {
        MethodSpec.Builder newBuilder = builder.beginControlFlow(statement, args);
        CodeBlock.Builder codeBuilder = CodeBlock.builder();
        method.apply(codeBuilder);
        newBuilder.addCode(codeBuilder.build());

        return newBuilder.endControlFlow();
    }


    /**
     * Description: Convenience method for adding [CodeBlock] statements without needing to do so every time.
     *
     * @author Andrew Grosner (fuzz)
     * @param builder builder
     * @param codeBlock codeBlock
     * @return CodeBlock Builder
     */
    public static CodeBlock.Builder statement(CodeBlock.Builder builder, CodeBlock codeBlock) {
        return builder.addStatement("$L", codeBlock);
    }

    public static MethodSpec.Builder statement(MethodSpec.Builder builder, CodeBlock codeBlock) {
        return builder.addStatement("$L", codeBlock);
    }

    public static <T extends Throwable> CodeBlock.Builder _catch(CodeBlock.Builder builder, Class<T> exception, Function<CodeBlock.Builder, CodeBlock.Builder> function) {
        return end(nextControl(builder, "catch", "$T e", function, exception), "");
    }

    private static CodeBlock.Builder nextControl(CodeBlock.Builder builder, String name, String statement, Function<CodeBlock.Builder, CodeBlock.Builder> function, Object... args) {
        return builder.nextControlFlow(name + (StringUtils.isNullOrEmpty(statement)? "" : " (" + statement + ")"), args)
            .add(function.apply(CodeBlock.builder()).build());
    }

    private static CodeBlock.Builder end(CodeBlock.Builder builder, String statement, Object... args) {
        return !StringUtils.isNullOrEmpty(statement)? builder.endControlFlow(statement, args) : builder.endControlFlow();
    }

    public static CodeBlock codeBlock(Function<CodeBlock.Builder, CodeBlock.Builder> function) {
        return function.apply(CodeBlock.builder()).build();
    }


}
